﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using TibcoQueueMonitor.Entities;
using TibcoQueueMonitor.Interfaces;

namespace TibcoQueueMonitor.TibcoAdminManagers
{
    /// <summary>
    /// Manager object that acts as the single point of contact for the UI.
    /// Responsible for coordinating and calling on other manager objects to
    /// execute their tasks.
    /// </summary>
    public class TibcoAdminManager : IDisposable
    {
        #region Constants

        #endregion //Constants

        #region Events
        /// <summary>
        /// Occurs when data is retrieved from the output of the
        /// tibemsadmin.exe application.
        /// </summary>
        /// <remarks>
        /// The event args returned from this event will return either parsed
        /// data or an exception if an error was encountered while retrieving
        /// this data.
        /// </remarks>
        public event EventHandler<AdminDataEventArgs> AdminDataRetrieved;

        #endregion //Events

        #region Private Fields
        private string _hostname;
        private string _port;
        private string _username;
        private string _password;
        private bool _returnTestData;
        private BackgroundWorker _dataRetriever;
        IOfflineDataGenerator _offlineData;

        #endregion //Private Fields

        #region Public Properties
        /// <summary>
        /// Gets or sets the hostname.
        /// </summary>
        /// <value>The hostname.</value>
        public string Hostname
        {
            get { return _hostname; }
            set { _hostname = value; }
        }

        /// <summary>
        /// Gets or sets the port.
        /// </summary>
        /// <value>The port.</value>
        public string Port
        {
            get { return _port; }
            set { _port = value; }
        }

        /// <summary>
        /// Gets or sets the username.
        /// </summary>
        /// <value>The username.</value>
        public string Username
        {
            get { return _username; }
            set { _username = value; }
        }

        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        /// <value>The password.</value>
        public string Password
        {
            get { return _password; }
            set { _password = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the manager should
        /// attempt to return offline data if a connection could not be
        /// established with the EMS server.
        /// </summary>
        /// <value><c>true</c> if offline data should be returned; otherwise,
        /// <c>false</c>.</value>
        public bool ReturnTestData
        {
            get { return _returnTestData; }
            set { _returnTestData = value; }
        }

        #endregion //Public Properties

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="TibcoAdminManager"/> class.
        /// </summary>
        public TibcoAdminManager(string hostname, string port, string user, string pass)
        {
            _hostname = hostname;
            _port = port;
            _username = user;
            _password = pass;
            _dataRetriever = null;
            _offlineData = null;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TibcoAdminManager"/> class.
        /// </summary>
        /// <param name="hostname">The hostname.</param>
        /// <param name="port">The port.</param>
        /// <param name="user">The user.</param>
        /// <param name="pass">The password.</param>
        /// <param name="offlineDataGenerator">The offline data generator
        /// that generates offline data in the event a connection cannot be
        /// established.</param>
        public TibcoAdminManager(string hostname, string port, string user, string pass, IOfflineDataGenerator offlineDataGenerator)
        {
            _hostname = hostname;
            _port = port;
            _username = user;
            _password = pass;
            _dataRetriever = null;
            _offlineData = offlineDataGenerator;
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="TibcoAdminManager"/> is reclaimed by garbage collection.
        /// </summary>
        ~TibcoAdminManager()
        {
            Dispose(false);
        }

        #endregion //Constructors

        #region Public Methods
        /// <summary>
        /// Calls the configured EMS server.
        /// </summary>
        /// <remarks>
        /// This method makes an asynchronous call to initiate the
        /// tibemsadmin.exe executable. The results of this execution are
        /// returned via the <c>AdminDataRetrieved</c> event, which must be
        /// subscribed to.
        /// </remarks>
        public void StartRetrieveAdministrationData()
        {
            // Start background thread to retrieve data
            if (_dataRetriever != null)
            {
                _dataRetriever.Dispose();
            }
            _dataRetriever = new BackgroundWorker();
            _dataRetriever.DoWork += new DoWorkEventHandler(dataRetriever_DoWork);
            _dataRetriever.RunWorkerCompleted += new RunWorkerCompletedEventHandler(dataRetriever_RunWorkerCompleted);

            _dataRetriever.RunWorkerAsync();
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion //Public Methods

        #region Protected Methods
        /// <summary>
        /// Called when administration data has been retrieved.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <remarks>
        /// The data parameter may contain either an instantiated
        /// TibcoAdministrationData object, or an Exception if a failure was
        /// encountered while retrieving the data. The base functionality
        /// checks what type of object is passed through, and raises an event
        /// with either an instance of TibcoAdministrationData, an exception,
        /// or null.
        /// </remarks>
        protected virtual void OnAdminDataRetrieved(object data)
        {
            if (AdminDataRetrieved != null)
            {
                if (data is TibcoAdministrationData)
                {
                    AdminDataRetrieved(this, new AdminDataEventArgs(data as TibcoAdministrationData));
                }
                else
                {
                    AdminDataRetrieved(this, new AdminDataEventArgs(data as Exception));
                }
            }
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_dataRetriever != null)
                {
                    _dataRetriever.Dispose();
                }
            }
        }

        #endregion //Protected Methods

        #region Private Methods
        private void dataRetriever_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                e.Result = GetAdministrationData();
            }
            catch (Exception ex)
            {
                e.Result = ex;
            }
        }

        private void dataRetriever_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            OnAdminDataRetrieved(e.Result);
        }

        private TibcoAdministrationData GetAdministrationData()
        {
            string adminParameters = String.Format(CultureInfo.InvariantCulture, "-server tcp://{0}:{1} -user {2} -password {3} -script binaries\\show_queues.script", _hostname, _port, _username, _password);
            Process adminProc = null;
            ProcessStartInfo startInfo = new ProcessStartInfo(@"binaries\tibemsadmin.exe", adminParameters);
            startInfo.RedirectStandardOutput = true;
            startInfo.UseShellExecute = false;
            startInfo.CreateNoWindow = true;

            if (File.Exists(startInfo.FileName))
            {
                adminProc = Process.Start(startInfo);
            }
            else
            {
                throw new TibcoAdminFileNotFoundException("Unable to find tibcoemsadmin.exe required to execute query. See documentation for troubleshooting.", @"binaries\tibcoemsadmin.exe");
            }

            TibcoAdministrationData data = new TibcoAdministrationData();

            try
            {
                using (StreamReader sr = adminProc.StandardOutput)
                {
                    data = TibcoAdminDataParser.ParseStream(sr);
                }
            }
            catch
            {
                if (_returnTestData && _offlineData != null)
                {
                    data = _offlineData.GenerateOfflineTestData();
                    if (!data.IsTestData)
                    {
                        throw new InvalidOperationException("Offline Data Module Error: Offline data was generated by configured module, but is not marked in code as test data.");
                    }
                }
                else
                {
                    throw;
                }
            }

            return data;
        }


        #endregion //Private Methods
    }
}
